iT邦幫忙

2026 iThome 鐵人賽

DAY 9
0

學 C 或 C++ 之類會處理記憶體位址的程式語言,最令人害怕的莫過於「指標」,現在就讓我們深吸一口氣、準備好清晰的頭腦與十足的耐心,來好好面對這萬惡的指標。

前情提要

在接觸指標時,我們會先認識兩種符號:*&

& 比較簡單,就理解為「的記憶體位址」,例如 &value 就是 value 的記憶體位址。

至於 *,可分為兩種情況:

  1. 前面有接如 intchar 之類的資料型別,代表是指標,存的是記憶體位址。
  2. 如果前面什麼也沒有,表示去某個記憶體位址取值。

三者統整如下:

int a = 10       // 宣告一整數變數 a,其值為 10
int * p = &a;    // p 為指標,指向變數 a 的記憶體位址(也就是 p 存的是 a 變數的記憶體位址)
*p               // 到 p 指標存的記憶體位址取值

所以假設電腦給變數 a 的記憶體位址是 0x001,那 p 存的值就是 0x001,而 *p 就是去 0x001 這個記憶體空間看內容是什麼,可得為 10

本篇文章看到的 * 前面都會加上資料型別,因此都是指標而非取值。

第一部分:基本認識

一共分為:

  1. 整數變數
  2. 指向整數的指標
  3. 指標的指標

整數變數

首先來看最簡單的:

int a = value;

這代表變數 a 本身的資料型別是 int、值為 value,記憶體會分配空間來儲存 value 這個整數。

指向整數的指標

如果多了一個 *,前面又有資料型別,例如:

int *a = &value;

這時 a 就變成一個「指標變數」,存的是 value 的記憶體位址(用 &value 表示),而非存 value 的值。

指標的指標

上面說 int *a = &value 的寫法會讓 a 存放 value 的記憶體位址,也就是 &value,但這個 a 變數也有自己的記憶體位址。

如果我們還要另外一個指標變數表示 a 這個指標變數的記憶幾位址,就要再加一顆 * 如下:

int **aa = &a

這個 aa 指標變數存放的便是 a 這個指標變數的記憶體位址。

第一部分綜合比較

在第一部分我們知道:

  1. 沒有 * 就代表變數本身。
  2. 每多一顆 * 就多一層指標(因為指標本身也有自己的記憶體位址)。
int a = 1        // a 是變數 1
int *a = &1;     // a 是指向「變數 1 記憶體位址」的指標
int **aa = &a;   // aa 是指向「a 指標記憶體位址」的指標

第二部分:陣列與指標

一共分為:

  1. 陣列
  2. 元素為指標的陣列
  3. 指向陣列的指標

陣列

假設陣列有 3 個元素、每個元素都是整數,則寫法為:

int a[3] = {0, 1, 2}; // 陣列每個元素都是整數

這代表 a 是一個共有三個元素的陣列,每個陣列元素的資料型別都是 int,並且已經分別賦值為 012

元素為指標的陣列

剛剛 a 陣列裡面的元素是整數,但若將 a 陣列中的元素改為指標,而這些陣列內的指標們指向整數,寫法為:

int x = 0, y = 1, z = 2;
int *a[3] = {&x, &y, &z}; // 陣列每個元素都是指標,指向整數

a[0]a[1]a[2] 的資料型別都是指標變數 int *,存放的值分別為 &x&y&z

指向陣列的指標

如果把陣列裡的元素都改回整數,要去取陣列位址,則為:

int arr[3] = {1, 2, 3};
int (*a)[3] = &arr;

這個 a 存的是 arr 陣列的位址,或者說就是指向 arr 的指標。

第二部分綜合比較

在第二部分我們知道:

  1. 沒有 * 就是陣列。
  2. * 直接貼在陣列上,表示陣列裡每個元素都是指標。
  3. * 先被 () 包起來,表示指向陣列的指標。

假設長度都為 3

int a[3] = {0, 1, 2};      // 元素為整數、名為 a 的陣列
int *a[3] = {&x, &y, &z};  // a 陣列元素皆為指標
int (*a)[3] = &arr;        // a 是指向整數陣列 arr 的指標

我自己的理解是沒括弧時,看成是把 int * 分配給陣列每個元素,因此陣列的每個元素都要是指標。

而若是要指向陣列、且陣列裡的每個元素都變成指標,寫法就是:

int *arr[3] = {&x, &y, &z}; // arr 陣列元素皆為指標
int *(*a)[3] = &arr;        // 指標 a 指向 arr 陣列

第一個括弧外的 * 搭配 int,表示 arr 陣列裡面的每個元素都是指標;第二個括弧內的 * 則是指向 arr 陣列的指標 a,存的是 arr 的記憶體位址 &arr

第三部分:函數與指標

剛剛陣列用[] 表示,現在要處理的是函數,用() 表示,一共分為:

  1. 指向函數的指標
  2. 元素為指向函數指標的陣列

指向函數的指標

如果指標變數指向一個像下面這樣,參數跟回傳值都是整數型別的函數:

int foo(int x){
    return x * 2;
}

指向 foo() 函數的指標變數寫法為:

int (*a)(int) = foo;

前面說 int (*a)[3] 代表 a 是指向陣列的指標,這裡構造相似,只是 (*a) 後面從 [3] 變成 (int),表示接收整數型別參數的函數,a 指標指向這個函數,而函數回傳的值型別寫在 (*a) 前面,也是 int ,因此為整數。

元素為指向函數指標的陣列

剛剛的指標直接指向一個函數,但如果改成指向一個陣列,陣列的元素則通通為指向函數的指標,則這個陣列為:

int (*a[3])(int) = {foo, bar, baz}

跟剛剛的 int (*a)(int) 相比,這邊多了一個 [3] 接在 a 後面,表示 a 是一個包含三個元素的陣列(想成 int a[3]])。

只是前面多了 *,表示陣列裡面裝的是指標;後面多了 (int),表示陣列中的指標指向參數為整數的函數。

我們可以透過索引值去取得特定函數,例如:

int (*a[3])(int) = {foo, bar, baz}
a[0] = foo;
int result = a[0](5);

第三行取 a[0] 表示去陣列取第 0 個元素,為指向 foo() 的指標,提供的參數值為 5,因此 a[0](5) 等同於 foo(5)

第三部分綜合比較

第三部分處理的是函數,會看到 (),單純指向一個函數的指標比較簡單,就是:

int (*a)(int);

上面的 a 代表「指向函數的指標」。

但如果改成一陣列、陣列元素為指向函數的指標,就是:

int (*a[3])(int);

這裡的 a 是長度為 3 的陣列名字。

綜合比較

是不是有點頭昏?讓我們再複習一次:

int a               // 整數變數
int *a              // 指向整數的指標
int **aa            // 指標的指標(被指的指標指向整數)

int a[3]            // 陣列
int *a[3]           // 元素為指標的陣列
int (*a)[3]         // 指向陣列的指標(陣列元素為整數)
int *(*a)[3]        // 指向陣列的指標(陣列元素為指標)

int (*a)(int);      // 指向函數的指標(函數參數與回傳值皆為整數)
int (*a[3])(int);   // 元素為指標的陣列(每個指標都指向參數與回傳值皆為整數的函數)

上一篇
Day 8 - 隱含類型轉換的陷阱
下一篇
Day 10 - 指標與整數間的型別轉換
系列文
韌體工程師的不只 0x10 個問題19
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言